package com.database.objects;

import static com.load.parsers.LineParser.getLinesUsingDiff;
import static com.load.utils.ContentUtil.readLines;
import japa.parser.ast.body.Parameter;

import java.io.*;
import java.util.*;

import javax.persistence.*;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Table;

import org.hibernate.annotations.*;

import com.load.utils.*;

@Entity
@Table(name = "method")
public class Method extends Element<Class, LineOO> {

	private static final long serialVersionUID = 8742063749272282420L;

	@Id
	@Column(name = "met_id", nullable = false, unique = true)
	@GeneratedValue(generator = "seq", strategy = GenerationType.SEQUENCE)
	@SequenceGenerator(name = "seq", sequenceName = "seq_method", allocationSize = 1, initialValue = 1)
	private Integer id;

	@Column(name = "met_signature", nullable = false)
	private String signature;

	@Enumerated(EnumType.STRING)
	@Column(name = "met_status", nullable = false)
	private Status status;

	@org.hibernate.annotations.Type(type = "text")
	@Column(name = "met_content", nullable = false)
	private String content;

	@Column(name = "met_cyclomatic", nullable = false)
	private Double cyclomatic = 0.0;

	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "cla_id", nullable = false)
	private Class clazz;

	@CollectionOfElements
	@JoinTable(name = "variable", joinColumns = { @JoinColumn(name = "met_id", nullable = false) })
	private List<Variable> variables = new ArrayList<Variable>();

	@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "method")
	private List<LineOO> lines = new ArrayList<LineOO>();

	public Method() {

	}

	public Method(String signature, Status status, String content, Class clazz) {
		this.signature = signature;
		this.status = status;
		this.content = content;
		this.clazz = clazz;
	}

	public Method(String name, List<Parameter> parameters, Status status, Class clazz) {
		signature = constructSignature(name, parameters);
		this.status = status;
		this.clazz = clazz;
	}

	public static String constructSignature(String name, List<Parameter> parameters) {
		String signature = name;
		signature += "(";
		int i = 0;
		while (parameters != null && i < parameters.size()) {
			signature += i == 0 ? parameters.get(i) : ", " + parameters.get(i);
			i++;
		}
		signature += ")";
		return signature;
	}

	public Integer getId() {
		return id;
	}

	public String getSignature() {
		return signature;
	}

	@Override
	public Status getStatus() {
		return status;
	}

	@Override
	public void setStatus(Status status) {
		this.status = status;
	}

	@Override
	public Class getParent() {
		return clazz;
	}

	@Override
	public void setParent(Element<?, ?> parent) {
		clazz = (Class) parent;
	}

	public String getContent() {
		return content;
	}

	public Double getCyclomatic() {
		return cyclomatic;
	}

	public void addCyclomatic() {
		cyclomatic += 1;
	}

	public void addCyclomatic(int value) {
		cyclomatic += value;
	}

	public void setContent(String content) {
		try {
			content = ContentUtil.cleanContentOO(content);
			this.content = content;
		} catch (IOException e) {
			System.out.println("Error processing content method");
			e.printStackTrace();
		}
	}

	public List<Variable> getVariables() {
		return variables;
	}

	public void addVariable(Variable variable) {
		variables.add(variable);
	}

	@Override
	public List<LineOO> getChilds() {
		return lines;
	}

	@Override
	public void addChild(LineOO line) {
		lines.add(line);
	}

	@Override
	public String getLabel() {
		return signature;
	}

	@Override
	public Type getType() {
		return Type.METHOD;
	}

	public void addLinesUsingContent() {
		if (!hasChilds() && !content.equals("")) {
			List<String> lines = readLines(content);
			for (int i = 0; i < lines.size(); i++) {
				addChild(new LineOO(status, this));
			}
		}
	}

	public void addLinesUsingDiffContent(String diff) {
		if (!hasChilds()) {
			List<Element<?, ?>> linesDiff = getLinesUsingDiff(diff, LineOO.class);
			for (Element<?, ?> element : linesDiff) {
				LineOO line = (LineOO) element;
				line.setParent(this);
				lines.add(line);
			}
		}
	}

	public int getNumberVariables() {
		return variables.size();
	}

	public Variable getVariableByName(String name) {
		for (Variable variable : variables) {
			if (variable.getName().equals(name)) {
				return variable;
			}
		}
		return null;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + (signature == null ? 0 : signature.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (getClass() != obj.getClass()) {
			return false;
		}
		Method other = (Method) obj;
		if (signature == null) {
			if (other.signature != null) {
				return false;
			}
		} else if (!signature.equals(other.signature)) {
			return false;
		}
		return true;
	}

	@Override
	public String toString() {
		return id + " " + signature;
	}

}